#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <signal.h>

//哲学家数量
#define N_PHILOSOPHER	5
//筷子互斥量结构体
typedef struct chopstick {
	pthread_mutex_t mutex;
} chopstick_t;
//哲学家结构体
typedef struct philosopher {
	pthread_t tid;
	int num;
} philosopher_t;

//声明变量
static int 				n_philosopher;
static pthread_mutex_t 	stdout_mutex = PTHREAD_MUTEX_INITIALIZER;
static chopstick_t 		*chopsticks;
static philosopher_t 	*philosophers;

static void* philosopher(void* args);

static int rand_int() {
	return rand() % 5 + 1;
}

//创建哲学家线程
static void philosophers_create() {
	int i;

	for (i = 0; i < n_philosopher; i++) {
		philosophers[i].num = i + 1;
		//创建哲学家线程，并把线程号白存在每个哲学家变量里，执行philosopher方法，传入参数为哲学家的号码
		pthread_create(&philosophers[i].tid, NULL, philosopher, &philosophers[i].num);
	}
}

static void philosophers_wait() {
	int i;

	for (i = 0; i < n_philosopher; i++) {
		pthread_join(philosophers[i].tid, NULL);
	}
}

//初始化筷子和每根筷子互斥变量
static void chopsticks_init() {
	int i;

	for (i = 0; i < n_philosopher; i++) {
		pthread_mutex_init(&chopsticks[i].mutex, NULL);
	}
}


//释放，销毁筷子互斥量内存
static void chopsticks_free() {
	int i;

	for (i = 0; i < n_philosopher; i++) {
		pthread_mutex_destroy(&chopsticks[i].mutex);
	}
}

//哲学家申请拿筷子
static int take_chopstick(int num) {
	return pthread_mutex_trylock(&chopsticks[num].mutex);
}
//哲学家放筷子
static void put_chopstick(int num) {
	pthread_mutex_unlock(&chopsticks[num].mutex);
}

//哲学家吃饭
static void* philosopher(void* args) {
	int num = *(int*)args;

	while (1) {
		pthread_mutex_lock(&stdout_mutex);
		printf("philosopher %d is thinking...\n", num);
		pthread_mutex_unlock(&stdout_mutex);

		sleep(rand_int());

		if (take_chopstick(num) == 0) {
			if (take_chopstick((num + 1) % n_philosopher) == 0) {
				pthread_mutex_lock(&stdout_mutex);
				printf("philosopher %d is eating...\n", num);
				pthread_mutex_unlock(&stdout_mutex);

				sleep(rand_int());

				put_chopstick(num);
				put_chopstick((num + 1) % n_philosopher);
			}
			else { 
				//如果没拿到第二根筷子，就放下第一根筷子，避免死锁
				put_chopstick(num);
			}
		}
	}

	return NULL;
}

void int_handler(int sig) {
	int i;

	for (i = 0; i < n_philosopher; i++) {
		pthread_cancel(philosophers[i].tid);
	}

	philosophers_wait();
	chopsticks_free();

	free(chopsticks);
	free(philosophers);

	printf("exit\n");

	exit(0);
}

//主程序入口
int main(int argc, char* argv[]) {
	struct sigaction act;

	sigemptyset(&act.sa_mask);
	act.sa_handler = int_handler;
	act.sa_flags = 0;
	sigaction(SIGINT, &act, NULL);

	srand(2);

	n_philosopher = N_PHILOSOPHER;
	if (argc > 1) {
		n_philosopher = atoi(argv[1]);
	}

	chopsticks = (chopstick_t*)malloc(n_philosopher * sizeof(chopstick_t));
	if (!chopsticks) {
		fprintf(stderr, "%s\n", "chopsticks: malloc failed");
		exit(-1);
	}

	philosophers = (philosopher_t*)malloc(n_philosopher * sizeof(philosopher_t));
	if (!philosophers) {
		fprintf(stderr, "%s\n", "philosophers: malloc failed");
		exit(-1);
	}

	chopsticks_init();
	philosophers_create();

	while (1) {
		sleep(3);
	}

	return 0;
}